package com.xiaouyudeguang.common.config;

import com.fasterxml.jackson.core.type.TypeReference;
import com.xiaouyudeguang.common.client.ClientInfo;
import com.xiaouyudeguang.common.exception.BusinessException;
import com.xiaouyudeguang.common.permit.PermitUrlBean;
import com.xiaouyudeguang.common.request.RequestDto;
import com.xiaouyudeguang.common.respose.ResultDto;
import com.xiaouyudeguang.common.utils.EnvironmentUtils;
import com.xiaouyudeguang.common.utils.JsonObject;
import com.xiaouyudeguang.common.utils.StringUtils;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Data
@Slf4j
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties("spring.security")
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private static final RestTemplate restTemplate = new RestTemplate();

    protected String url;

    protected String clientId;

    protected String clientSecret;

    protected String clientSign;

    protected String clientMode;

    protected List<String> ignoreUrls = new ArrayList<>();

    protected List<String> blankIps = new ArrayList<>();

    @Autowired(required = false)
    private TokenStore tokenStore;

    @Autowired(required = false)
    private PermitUrlBean permitUrlBean;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        if (tokenStore == null) {
            try {
                ClientInfo clientInfo = this.getClient();
                if (clientInfo == null) {
                    throw new BusinessException("未授权");
                }
                RemoteTokenServices tokenServices = new RemoteTokenServices();
                RestTemplate restTemplate = new RestTemplate();
                restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
                    @Override
                    // Ignore 400
                    public void handleError(ClientHttpResponse response) throws IOException {
                        if (response.getRawStatusCode() != 400) {
                            super.handleError(response);
                        }
                    }
                });
                tokenServices.setRestTemplate(restTemplate);
                JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
                converter.setSigningKey(clientInfo.getClientId());
                tokenServices.setAccessTokenConverter(converter);
                tokenServices.setCheckTokenEndpointUrl(clientInfo.getClientUrl());
                tokenServices.setClientId(clientInfo.getClientId());
                tokenServices.setClientSecret(clientInfo.getClientSecret());
                resources.tokenServices(tokenServices).stateless(true);
            } catch (Exception ignored){

            }
        }
    }

    public ClientInfo getClient() throws Exception {
        String response = restTemplate.postForObject(url, RequestDto.getRequest(ClientInfo.builder().clientId(clientId).clientSecret(clientSecret).clientSign(clientSign).build()), String.class);
        return StringUtils.isBlank(response) ? null : ResultDto.getResult(JsonObject.fromJson(response, new TypeReference<ResultDto<ClientInfo>>() {
        }));
    }

    @Override
    @SneakyThrows
    public void configure(HttpSecurity httpSecurity) {
        httpSecurity.headers().frameOptions().disable();
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
        if (EnvironmentUtils.isLocal() && StringUtils.isBlank(clientMode)) {
            registry.anyRequest().permitAll().and().csrf().disable();
        } else {
            ignoreUrls.forEach(url -> registry.antMatchers(url).permitAll());
            permitUrlBean.getIgnoreUrls().forEach(url -> registry.antMatchers(url).permitAll());
            registry.anyRequest().authenticated().and().csrf().disable();
        }
    }
}

